package com.wen.system.shiro.realm;

import com.wen.common.utils.JwtUtil;
import com.wen.common.utils.RedisUtil;
import com.wen.common.pojo.system.Permission;
import com.wen.common.pojo.system.User;
import com.wen.system.jwt.JwtToken;
import com.wen.system.service.UserService;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private EhCacheManager ehCacheManager;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedisUtil redisUtil;

//    @Override
//    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
//        super.setCredentialsMatcher(new AllowPassword());
//    }

    @Override
    public boolean supports(AuthenticationToken token) {
        // 判断左边是否是右边的实例 （多realm的话，realm中支持的哪个token来判断使用哪个realm）
        return token instanceof JwtToken;
    }

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
        User loginUser = (User) subject.getPrincipal();

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();


        //设置角色-------
        Set<String> roleNames = userService.getRoleNameByUserId(loginUser.getId());
//        roleNames.add("admin");// 给用户添加这个角色，shiro过滤器根据这个字段过滤
        if (!CollectionUtils.isEmpty(roleNames)) {
            //交给shiro
            authorizationInfo.addRoles(roleNames);
        }

        //设置权限---------如果权限字段为空，shiro会报 IllegalArgumentException异常，权限暂定按permission表中的path路径
        List<Permission> permissions = userService.getPermissionByUserId(loginUser.getId());
        List<String> perms = permissions.stream().map(Permission::getPerms).collect(Collectors.toList());
        perms.forEach(perm -> {
            authorizationInfo.addStringPermission(perm); // 必须要以这种格式[user:add]
            System.out.println(perm);
        });
//        authorizationInfo.addStringPermissions(pathPers);
//          or
//        permissions.stream().map(Permission::getPath).forEach(authorizationInfo::addStringPermission);

        return authorizationInfo;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.info("realm... authentication");
        // 前端进来的账号密码（token）  --（shiro 的AuthenticationToken实例）
//        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // 验证Jwt
        String jwt = String.valueOf(token.getPrincipal());
        // 获取用户身份宣称
//        Claims claim = jwtUtil.getClaimByJwt(jwt);
        Claims claim = jwtUtil.verifyJwt(jwt, jwtUtil.getSecret());
        if (claim == null || jwtUtil.isJwtExpired(jwt)) {
            throw new ExpiredCredentialsException("用户过期，请重新登录");
        }

        String username = claim.getSubject();
        //按名字查询出来的用户
//        User user = userService.getUserById(Long.valueOf(userId));
        User user = userService.findUserByName(username);
//        int i =1/0;
        if (user != null) {
            beforeAuthc(user);
            // 认证通过
            user.setToken(jwt);
            Subject subject = SecurityUtils.getSubject();
            Cache<Object, Object> cache = ehCacheManager.getCache("logUser");
            cache.put("session_id=" + subject.getSession().getId(), subject.getSession());

            return new SimpleAuthenticationInfo(user, token.getCredentials(), getName());
        }
        return null;
    }

    private void beforeAuthc(User user) {
        if ("1".equals(user.getDelFlag())) {
            // 自定义的异常抛到shiro中了，全局异常捕获shiro总异常
            throw new DisabledAccountException();
        }
        if ("0".equals(user.getStatus())) {
            // 自定义的异常抛到shiro中了，全局异常捕获shiro总异常
            throw new LockedAccountException();
        }
    }
}
